﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using Flowertwig.Utils.Hashing;
using Setup.Entities.AppFinder;

namespace Setup.Helpers
{
    public class AppFinderHelper
    {
        public AppInfo Info { get; set; }

        private WebClient client = new WebClient();
        private bool hasBeenCompleted;
        private bool needInstallation;
        private string installedPath;

        public event EventHandler<AppFinderHelperEventArgs> Update;

        public AppFinderHelper()
        {
            Update += OnUpdate;
            client.DownloadFileCompleted += client_DownloadFileCompleted;
            client.DownloadProgressChanged += client_DownloadProgressChanged;
        }

        void OnUpdate(object sender, AppFinderHelperEventArgs e) { }

        void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            FireUpdate(AppFinderStatus.DownloadingProgress, e.ProgressPercentage);
        }

        void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            hasBeenCompleted = true;
            if (e.Error != null)
            {
                FireUpdate(AppFinderStatus.ErrorWhenDownloading, message: e.Error.Message);
            }
            else
            {
                FireUpdate(AppFinderStatus.DownloadingIsFinished);
            }
        }

        public void DoWork()
        {
            const int maxIterations = 5;
            int currentIteration = 0;

            //while (!IsInstalled() && currentIteration <= maxIterations)
            if (!IsInstalled())
            {
                currentIteration++;
                // Download data if not on disk.
                while (!HasValidFileOnDisk())
                {
                    var downloadUrl = new Uri(Info.ApplicationDownloadUrl);

                    hasBeenCompleted = false;
                    FireUpdate(AppFinderStatus.DownloadingStarted);
                    client.DownloadFileAsync(downloadUrl, Info.TemporaryFileName);

                    do
                    {
                        System.Threading.Thread.Sleep(100);
                    } while (!hasBeenCompleted);
                }
                needInstallation = true;
                FireUpdate(AppFinderStatus.ActivateInstallation);
            }

            if (!needInstallation && currentIteration <= maxIterations)
            {
                FireUpdate(AppFinderStatus.InstallationFound, message: GetInstallationPath());
            }
        }

        public string GetInstallationPath()
        {
            if (!string.IsNullOrEmpty(installedPath))
                return installedPath;

            // If we have no search path and Temporary file exist, it doesn't need to be installed.
            if (string.IsNullOrEmpty(Info.InstallationSearchPattern) && File.Exists(Info.TemporaryFileName))
            {
                installedPath = Info.TemporaryFileName;
                return installedPath;
            }

            var searchPath = GetSearchPaths();

            foreach (var directory in searchPath)
            {
                try
                {
                    var subDirectories = Directory.GetDirectories(directory);

                    foreach (var subDirectory in subDirectories)
                    {
                        try
                        {
                            var files = Directory.GetFiles(subDirectory, Info.InstallationSearchPattern, SearchOption.AllDirectories);
                            if (files.Length > 0)
                            {
                                installedPath = files[0];
                                return installedPath;
                            }
                        }
                        catch
                        {
                            // Something went wrong for this directory
                        }
                    }
                }
                catch
                {
                    // Something went wrong for this directory
                }
            }
            return null;
        }

        public bool IsInstalled()
        {
            var path = GetInstallationPath();
            return !string.IsNullOrEmpty(path);
        }

        private List<string> GetSearchPaths()
        {
            var searchPaths = new List<string>();
            var x86ProgramFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);     // Returns "C:\Program Files (x86)"
            var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);           // Returns ""C:\Program Files (x86)" on my system..

            var x86String = " (x86)";
            if (programFiles.Equals(x86ProgramFiles) && programFiles.EndsWith(x86String))
            {
                programFiles = programFiles.Substring(0, programFiles.Length - x86String.Length);
            }

            searchPaths.Add(x86ProgramFiles);
            searchPaths.Add(programFiles);

            var drives = Directory.GetLogicalDrives();
            foreach (var drive in drives)
            {
                searchPaths.Add(drive + "RavenDB");
            }
            return searchPaths;
        }

        private bool HasValidFileOnDisk()
        {
            return IsValidFileHash();
        }

        protected void FireUpdate(AppFinderStatus status, int progress = 0, string message = null)
        {
            Update(this, new AppFinderHelperEventArgs {
                Status = status,
                Progress = progress,
                Message = message
            });
        }

        private bool IsValidFileHash()
        {
            if (!File.Exists(Info.TemporaryFileName))
                return false;

            FireUpdate(AppFinderStatus.VerifyingDownloadedFile);
            var tth = new TthThreaded(Info.TemporaryFileName);
            var id = Convert.ToBase64String(tth.GetTTH_Value());
            var isEqual = Info.ApplicationFileHash.Equals(id);
            if (isEqual)
            {
                FireUpdate(AppFinderStatus.DownloadedFileWasSuccessfullyVerified);
            }
            else
            {
                FireUpdate(AppFinderStatus.DownloadedFilesWasNotVerified);
            }
            return isEqual;
        }

        public void Cancel()
        {
            client.CancelAsync();
        }
    }
}